using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Platform.Storage;
using Avalonia.VisualTree;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Point = Avalonia.Point;
using Rectangle = Avalonia.Controls.Shapes.Rectangle;
using System;
using System.IO;
using Avalonia;
using Avalonia.Media.Imaging;
using Avalonia.Controls.ApplicationLifetimes;
using MathPlus.ViewModels;
using MathPlus.Features.TextOCR;

namespace MathPlus.Features.ScreenShot
{
    public partial class ScreenShotView : Window
    {
        private Point startPoint;
        private bool isSelecting;
        private Rectangle? selectionRect;
        private Rectangle? topOverlay;
        private Rectangle? bottomOverlay;
        private Rectangle? leftOverlay;
        private Rectangle? rightOverlay;

        #region Windows API
        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr GetDC(IntPtr hwnd);

        [DllImport("user32.dll", SetLastError = true)]
        static extern bool ReleaseDC(IntPtr hwnd, IntPtr hdc);

        [DllImport("gdi32.dll", SetLastError = true)]
        static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest,
            IntPtr hdcSource, int xSrc, int ySrc, int rop);

        [DllImport("gdi32.dll", SetLastError = true)]
        static extern IntPtr CreateCompatibleDC(IntPtr hdc);

        [DllImport("gdi32.dll", SetLastError = true)]
        static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int width, int height);

        [DllImport("gdi32.dll", SetLastError = true)]
        static extern IntPtr SelectObject(IntPtr hdc, IntPtr hObject);

        [DllImport("gdi32.dll", SetLastError = true)]
        static extern bool DeleteObject(IntPtr hObject);

        [DllImport("gdi32.dll", SetLastError = true)]
        static extern bool DeleteDC(IntPtr hdc);

        [DllImport("gdi32.dll", SetLastError = true)]
        static extern int GetDIBits(IntPtr hdc, IntPtr hbmp, uint uStartScan, uint cScanLines,
            [Out] byte[] lpvBits, ref BITMAPINFO lpbmi, uint uUsage);

        [StructLayout(LayoutKind.Sequential)]
        struct BITMAPINFOHEADER
        {
            public uint biSize;
            public int biWidth;
            public int biHeight;
            public ushort biPlanes;
            public ushort biBitCount;
            public uint biCompression;
            public uint biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public uint biClrUsed;
            public uint biClrImportant;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct BITMAPINFO
        {
            public BITMAPINFOHEADER bmiHeader;
            public int[] bmiColors;
        }

        private const int SRCCOPY = 0x00CC0020;
        private const int DIB_RGB_COLORS = 0;
        #endregion

        #region X11 Imports
        [DllImport("libX11", SetLastError = true)]
        private static extern IntPtr XOpenDisplay(IntPtr display);

        [DllImport("libX11", SetLastError = true)]
        private static extern IntPtr XDefaultRootWindow(IntPtr display);

        [DllImport("libX11", SetLastError = true)]
        private static extern IntPtr XGetImage(IntPtr display, IntPtr drawable, int x, int y,
            uint width, uint height, ulong plane_mask, int format);

        [DllImport("libX11", SetLastError = true)]
        private static extern IntPtr XGetPixel(IntPtr ximage, int x, int y);

        [DllImport("libX11", SetLastError = true)]
        private static extern int XGetPixmapFormat(IntPtr display);

        [DllImport("libX11", SetLastError = true)]
        private static extern void XDestroyImage(IntPtr ximage);

        [DllImport("libX11", SetLastError = true)]
        private static extern void XCloseDisplay(IntPtr display);

        private const int ZPixmap = 2;
        private const ulong AllPlanes = ~0UL;
        #endregion

        #region Linux X11 API
        #endregion

        public ScreenShotView()
        {
            InitializeComponent();

            // Get UI elements
            selectionRect = this.FindControl<Rectangle>("SelectionRect");
            topOverlay = this.FindControl<Rectangle>("TopOverlay");
            bottomOverlay = this.FindControl<Rectangle>("BottomOverlay");
            leftOverlay = this.FindControl<Rectangle>("LeftOverlay");
            rightOverlay = this.FindControl<Rectangle>("RightOverlay");

            // Initialize overlay sizes
            var bounds = Screens.Primary?.Bounds ?? new PixelRect(0, 0, 800, 600);
            if (topOverlay != null && bottomOverlay != null && leftOverlay != null && rightOverlay != null)
            {
                topOverlay.Width = bounds.Width;
                bottomOverlay.Width = bounds.Width;
                leftOverlay.Height = bounds.Height;
                rightOverlay.Height = bounds.Height;
            }

            // Set up event handlers
            PointerPressed += OnPointerPressed;
            PointerMoved += OnPointerMoved;
            PointerReleased += OnPointerReleased;
            KeyDown += OnKeyDown;
        }

        private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
        {
            if (selectionRect == null) return;

            var point = e.GetPosition(this);
            isSelecting = true;
            startPoint = point;

            // Initialize selection rectangle
            Canvas.SetLeft(selectionRect, point.X);
            Canvas.SetTop(selectionRect, point.Y);
            selectionRect.Width = 0;
            selectionRect.Height = 0;
            selectionRect.IsVisible = true;

            UpdateOverlays(new Rect(point.X, point.Y, 0, 0));
        }

        private void OnPointerMoved(object? sender, PointerEventArgs e)
        {
            if (!isSelecting || selectionRect == null) return;

            var point = e.GetPosition(this);
            var x = Math.Min(startPoint.X, point.X);
            var y = Math.Min(startPoint.Y, point.Y);
            var width = Math.Abs(point.X - startPoint.X);
            var height = Math.Abs(point.Y - startPoint.Y);

            // Update selection rectangle
            Canvas.SetLeft(selectionRect, x);
            Canvas.SetTop(selectionRect, y);
            selectionRect.Width = width;
            selectionRect.Height = height;

            // Update overlay rectangles
            UpdateOverlays(new Rect(x, y, width, height));
        }

        private void UpdateOverlays(Rect selection)
        {
            if (topOverlay == null || bottomOverlay == null || leftOverlay == null || rightOverlay == null) return;

            var bounds = Screens.Primary?.Bounds ?? new PixelRect(0, 0, 800, 600);

            // Update top overlay
            Canvas.SetLeft(topOverlay, 0);
            Canvas.SetTop(topOverlay, 0);
            topOverlay.Height = selection.Y;

            // Update bottom overlay
            Canvas.SetLeft(bottomOverlay, 0);
            Canvas.SetTop(bottomOverlay, selection.Y + selection.Height);
            bottomOverlay.Height = bounds.Height - (selection.Y + selection.Height);

            // Update left overlay
            Canvas.SetLeft(leftOverlay, 0);
            Canvas.SetTop(leftOverlay, selection.Y);
            leftOverlay.Width = selection.X;
            leftOverlay.Height = selection.Height;

            // Update right overlay
            Canvas.SetLeft(rightOverlay, selection.X + selection.Width);
            Canvas.SetTop(rightOverlay, selection.Y);
            rightOverlay.Width = bounds.Width - (selection.X + selection.Width);
            rightOverlay.Height = selection.Height;
        }

        private async void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
        {
            if (!isSelecting || selectionRect == null) return;
            isSelecting = false;

            if (selectionRect.Width > 10 && selectionRect.Height > 10)
            {
                await CaptureScreenshot();
            }

            Close();
        }

        private void OnKeyDown(object? sender, KeyEventArgs e)
        {
            if (e.Key == Key.Escape)
            {
                Close();
            }
        }

        private unsafe WriteableBitmap CaptureScreenWindows(int x, int y, int width, int height)
        {
            var bitmap = new WriteableBitmap(new PixelSize(width, height), new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Unpremul);

            IntPtr desktopDC = GetDC(IntPtr.Zero);
            IntPtr memoryDC = CreateCompatibleDC(desktopDC);
            IntPtr hBitmap = CreateCompatibleBitmap(desktopDC, width, height);
            IntPtr oldBitmap = SelectObject(memoryDC, hBitmap);

            try
            {
                BitBlt(memoryDC, 0, 0, width, height, desktopDC, x, y, SRCCOPY);

                BITMAPINFO bmi = new BITMAPINFO
                {
                    bmiHeader = new BITMAPINFOHEADER
                    {
                        biSize = (uint)Marshal.SizeOf<BITMAPINFOHEADER>(),
                        biWidth = width,
                        biHeight = -height, // Top-down image
                        biPlanes = 1,
                        biBitCount = 32,
                        biCompression = 0,
                        biSizeImage = (uint)(width * height * 4)
                    },
                    bmiColors = new int[1]
                };

                var bitmapData = new byte[width * height * 4];
                GetDIBits(memoryDC, hBitmap, 0, (uint)height, bitmapData, ref bmi, DIB_RGB_COLORS);

                using (var lockedBitmap = bitmap.Lock())
                {
                    var backBuffer = (byte*)lockedBitmap.Address;
                    fixed (byte* ptr = bitmapData)
                    {
                        Buffer.MemoryCopy(ptr, backBuffer, bitmapData.Length, bitmapData.Length);
                    }
                }
            }
            finally
            {
                // Clean up resources
                SelectObject(memoryDC, oldBitmap);
                DeleteObject(hBitmap);
                DeleteDC(memoryDC);
                ReleaseDC(IntPtr.Zero, desktopDC);
            }

            return bitmap;
        }

        private unsafe WriteableBitmap CaptureScreenLinux(int x, int y, int width, int height)
        {
            var bitmap = new WriteableBitmap(new PixelSize(width, height), new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Unpremul);

            IntPtr display = XOpenDisplay(IntPtr.Zero);
            if (display == IntPtr.Zero)
            {
                throw new Exception("Failed to open X display");
            }

            try
            {
                IntPtr root = XDefaultRootWindow(display);
                IntPtr image = XGetImage(display, root, x, y, (uint)width, (uint)height, AllPlanes, ZPixmap);

                if (image != IntPtr.Zero)
                {
                    try
                    {
                        using (var lockedBitmap = bitmap.Lock())
                        {
                            var backBuffer = (byte*)lockedBitmap.Address;
                            var stride = width * 4;

                            // 使用逐像素复制来避免内存访问错误
                            for (int py = 0; py < height; py++)
                            {
                                for (int px = 0; px < width; px++)
                                {
                                    var pixel = XGetPixel(image, px, py);
                                    var offset = (py * stride) + (px * 4);
                                    
                                    backBuffer[offset] = (byte)(pixel & 0xFF);         // Blue
                                    backBuffer[offset + 1] = (byte)((pixel >> 8) & 0xFF);  // Green
                                    backBuffer[offset + 2] = (byte)((pixel >> 16) & 0xFF); // Red
                                    backBuffer[offset + 3] = 255;                          // Alpha
                                }
                            }
                        }
                    }
                    finally
                    {
                        XDestroyImage(image);
                    }
                }
            }
            finally
            {
                XCloseDisplay(display);
            }

            return bitmap;
        }

        private WriteableBitmap CaptureScreen(int x, int y, int width, int height)
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                return CaptureScreenWindows(x, y, width, height);
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                return CaptureScreenLinux(x, y, width, height);
            }
            else
            {
                throw new PlatformNotSupportedException("Screenshot capture is only supported on Windows and Linux");
            }
        }

        private async Task CaptureScreenshot()
        {
            if (selectionRect == null || !selectionRect.IsVisible) return;

            try
            {
                // Get screen scaling factor
                var screen = Screens.Primary;
                var scaling = screen?.Scaling ?? 1.0;

                // Get window position
                var position = this.Position;

                // Calculate actual screen coordinates
                var x = (int)((Canvas.GetLeft(selectionRect) + position.X) * scaling);
                var y = (int)((Canvas.GetTop(selectionRect) + position.Y) * scaling);
                var width = (int)(selectionRect.Width * scaling);
                var height = (int)(selectionRect.Height * scaling);

                // Hide selection and overlays before capture
                selectionRect.IsVisible = false;
                if (topOverlay?.IsVisible == true) topOverlay.IsVisible = false;
                if (bottomOverlay?.IsVisible == true) bottomOverlay.IsVisible = false;
                if (leftOverlay?.IsVisible == true) leftOverlay.IsVisible = false;
                if (rightOverlay?.IsVisible == true) rightOverlay.IsVisible = false;

                // Ensure UI updates are rendered
                await Task.Delay(100);

                // Capture the screen
                var bitmap = CaptureScreen(x, y, width, height);
                
                // Generate filename with timestamp
                var fileName = $"Screenshot_{DateTime.Now:yyyyMMdd_HHmmss}.png";
                var filePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
                using (var stream = File.OpenWrite(filePath))
                {
                    bitmap.Save(stream);
                }

                // Update TextOCRViewModel
                if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
                {
                    if (desktop.MainWindow?.DataContext is MainWindowViewModel mainVM)
                    {
                        var textOCRVM = mainVM.DemoPages.Where(x => x is TextOCRViewModel).FirstOrDefault() as TextOCRViewModel;
                        if (textOCRVM != null)
                        {
                            // Create a new bitmap that won't be disposed
                            textOCRVM.SelectedImage = new Bitmap(filePath);
                        }
                    }
                }

                Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error capturing screenshot: {ex.Message}");
            }
        }
    }
}